package cn.codeartist.springboot.component.http.bean;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

/**
 * 请求包装类
 * 将对象或者Map请求参数，转换成QS格式：a=b&c=d&c=e
 *
 * @author 艾江南
 * @date 2019/7/11
 */
public final class RequestWrapper {

    final static private Logger logger = LoggerFactory.getLogger(RequestWrapper.class);

    final static public String QS_SEP_START = "?";

    final static public String QS_SEP_AND = "&";

    final static public String NAME_VALUE_SEPARATOR = "=";

    /**
     * 将对象转换成URL序列化字符串
     *
     * @param body   请求体对象
     * @param encode 是否URL编码
     * @return 字符串
     */
    @SuppressWarnings("unchecked")
    public static String serializeParam(Object body, boolean encode) {
        StringBuilder builder = new StringBuilder();
        if (body == null) {
            return "";
        }
        // 处理Map
        if (body instanceof Map) {
            ((Map<String, ?>) body).forEach((key, value) -> toQueryString(builder, key, value, encode));
            return builder.toString();
        }
        // 处理对象
        parseObjectField(body.getClass(), body, builder, encode);
        return builder.toString();
    }

    /**
     * 生成查询字符串
     *
     * @param builder 字符串
     * @param key     键
     * @param value   值
     * @param encode  是否URL编码
     */
    private static void toQueryString(StringBuilder builder, String key, Object value, boolean encode) {
        if (value instanceof Collection<?>) {
            ((Collection<?>) value).forEach(val -> {
                if (builder.length() != 0) {
                    builder.append(QS_SEP_AND);
                }
                builder.append(key).append(NAME_VALUE_SEPARATOR).append(queryStringWrapper(val, encode));
            });
        } else {
            if (builder.length() != 0) {
                builder.append(QS_SEP_AND);
            }
            builder.append(key).append(NAME_VALUE_SEPARATOR).append(queryStringWrapper(value, encode));
        }
    }

    /**
     * 递归调用 获取对象所有字段的值
     *
     * @param clazz      类型
     * @param bodyObject 类
     * @param builder    拼接字符串
     * @param encode     是否URL编码
     */
    private static void parseObjectField(Class<?> clazz, Object bodyObject, StringBuilder builder, boolean encode) {
        if (clazz == null) {
            return;
        }
        Field[] fields = clazz.getDeclaredFields();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                Object value = field.get(bodyObject);
                field.setAccessible(false);
                toQueryString(builder, field.getName(), value, encode);
            }
        } catch (IllegalAccessException e) {
            logger.error("Object serialize error. {}", e.getMessage(), e);
        }
        parseObjectField(clazz.getSuperclass(), bodyObject, builder, encode);
    }

    /**
     * 对参数类型进行处理并转换成字符串
     * 1. 将时间类型转换成毫秒数
     *
     * @param o      入参
     * @param encode 是否URL编码
     * @return 返回参数
     */
    private static String queryStringWrapper(Object o, boolean encode) {
        if (o == null) {
            return "";
        }
        if (o instanceof Date) {
            o = ((Date) o).getTime();
        }
        if (encode) {
            try {
                return URLEncoder.encode(o.toString(), StandardCharsets.UTF_8.name());
            } catch (Exception e) {
                throw new RuntimeException("http proxy url encoder error.", e);
            }
        }
        return o.toString();
    }
}
